package com.qianxun.framework.scheduler;

import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;

import java.util.Date;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

/**
 *         依赖于Quartz 框架
 *         <dependency>
 *             <groupId>org.quartz-scheduler</groupId>
 *             <artifactId>quartz</artifactId>
 *             <version>2.3.0</version>
 *         </dependency>
 * @author: huwei
 * @date: 2020/3/30 9:29
 * @version: 1.0.0
 */
public class SchedulerUtil {
//    //测试
//    public static void main(String[] args) throws SchedulerException {
//        schedule(()->{System.err.println(111);});
//
//        schedule(()->{System.err.println(222);} ,1 , 1 ,1);
//
//        schedule(()->{System.err.println(333); return "3-call";} ,(k)->{System.out.println(k);});
//
//
//        schedule(()->{System.err.println(333); return "4-call";} ,(k)->{System.out.println(k);} ,2 ,2 ,2);
//
//        schedule((k)->{System.err.println(k);} ,"555");
//
//        schedule((k)->{System.err.println(k);} ,"haha 5",3 , 3 ,3);
//
//        schedule((k)->{System.err.println(k); return "6-call";} ,"haha 6",(k)->{System.out.println(k);});
//
//
//        schedule((k)->{System.err.println(k); return "77-call";} ,"haha 777",(k)->{System.out.println(k);} ,2 ,2 ,2);
//    }

    private static final Set<TriggerKey> CLEANER_TRIGGER_KEY = new HashSet<>();
    /**
     * 任务调度器，空闲时间；即当没有任务后的等待时间；默认1小时后，关闭任务调度器
     */
    private static final long SCHEDULER_FREE_TIME = TimeUnit.MINUTES.toMillis(10);

    public static JobKey schedule(NoCallExecuteFunction task) throws SchedulerException {
        return schedule(task ,0 , 0,1);
    }

    public static JobKey schedule(NoCallExecuteFunction task ,long delayStartMillis ,int cycleSeconds,int repeatCount) throws SchedulerException {
        JobDetail jobDetail = JobUtil.newBuilder().withKey(randomJobKey()).withJob(task).build();
        return schedule(jobDetail, simpleTrigger(jobDetail ,delayStartMillis ,cycleSeconds ,repeatCount));
    }

    public static <T> JobKey schedule(NoCallParameterExecuteFunction<T> task ,T arg) throws SchedulerException {
        return schedule(task ,arg ,0 ,0 ,1);
    }

    public static <T> JobKey schedule(NoCallParameterExecuteFunction<T> task ,T arg ,long delayStartMillis ,int cycleSeconds ,int repeatCount) throws SchedulerException {
        JobDetail jobDetail = JobUtil.newBuilder().withKey(randomJobKey()).withJob(task ,arg).build();
        return schedule(jobDetail, simpleTrigger(jobDetail ,delayStartMillis ,cycleSeconds ,repeatCount));
    }

    public static <R> JobKey schedule(ExecuteFunction<R> task , CallFunction<R> call) throws SchedulerException {
        return schedule(task ,call ,0 ,0 ,1);
    }

    public  static <R> JobKey schedule(ExecuteFunction<R> task , CallFunction<R> call , long delayStartMillis , int cycleSeconds , int repeatCount) throws SchedulerException {
        JobDetail jobDetail = JobUtil.newBuilder().withKey(randomJobKey()).withJob(task ,call).build();
        return schedule(jobDetail, simpleTrigger(jobDetail ,delayStartMillis ,cycleSeconds ,repeatCount));
    }

    public static <T ,R> JobKey schedule(ParameterExecuteFunction<T ,R> task, T arg , CallFunction<R> call) throws SchedulerException {
        return schedule(task ,arg,call ,0 ,0 ,1);
    }

    public static <T ,R> JobKey schedule(ParameterExecuteFunction<T ,R> task, T arg , CallFunction<R> call , long delayStartMillis , int cycleSeconds , int repeatCount) throws SchedulerException {
        JobDetail jobDetail = JobUtil.newBuilder().withKey(randomJobKey()).withJob(task ,arg ,call).build();
        return schedule(jobDetail, simpleTrigger(jobDetail ,delayStartMillis ,cycleSeconds ,repeatCount));
    }

    public static JobKey schedule(JobDetail jobDetail ,Trigger trigger) throws SchedulerException {
        QuartzSchedulerManager.getOrCreate().scheduleJob(jobDetail, trigger);

        QuartzSchedulerManager.incrementAndGetJobCount();

        return jobDetail.getKey();
    }

    public static void shutdownJob(JobKey jobKey) throws SchedulerException {
        shutdownJob(jobKey ,SCHEDULER_FREE_TIME);
    }

    public static void shutdownJob(JobKey jobKey ,long exitGraceTimeMillis) throws SchedulerException {
        if(QuartzSchedulerManager.getOrCreate().checkExists(jobKey)){
            QuartzSchedulerManager.getOrCreate().pauseJob(jobKey);

            addJobCleaner(jobKey ,exitGraceTimeMillis);
        }
    }

    public static void shutdownNow() throws SchedulerException {
        QuartzSchedulerManager.getOrCreate().shutdown();
    }

    private static JobKey randomJobKey(){
        return new JobKey(UUID.randomUUID().toString() ,UUID.randomUUID().toString());
    }

    private static TriggerKey randomTriggerKey(){
        return new TriggerKey(UUID.randomUUID().toString() ,UUID.randomUUID().toString());
    }

    private static Trigger simpleTrigger(JobDetail jobDetail ,long delayStartMillis ,int cycleSeconds ,int repeatCount){
        TriggerBuilder triggerBuilder = TriggerBuilder.newTrigger();
        triggerBuilder.withIdentity(randomTriggerKey());
        if(delayStartMillis > 0){
            triggerBuilder.startAt(new Date(System.currentTimeMillis() + delayStartMillis));
        }else{
            triggerBuilder.startNow();
        }
        if(repeatCount > 0) {
            triggerBuilder.withSchedule(SimpleScheduleBuilder.repeatSecondlyForTotalCount(repeatCount ,cycleSeconds));
        }else{
            triggerBuilder.withSchedule(SimpleScheduleBuilder.repeatSecondlyForever(cycleSeconds));
        }
        triggerBuilder.forJob(jobDetail);
        return triggerBuilder.build();
    }

    /**
     * 添加任务清理任务
     * @param deleteJobKey 任务key
     * @throws SchedulerException
     */
    private static void addJobCleaner(JobKey deleteJobKey ,long exitGraceTimeMillis) throws SchedulerException {
        final JobKey innerDeleteJobKey = deleteJobKey;
        JobDetail jobDetail = JobUtil.newBuilder().withKey(randomJobKey()).withJob(()->{
            QuartzSchedulerManager.getOrCreate().deleteJob(innerDeleteJobKey);
            //任务数量减一
            int jobCount = QuartzSchedulerManager.decrementAndGetJobCount();
            //当任务小于0时，终止任务
            if(jobCount <= 0){
                QuartzSchedulerManager.reset();
            }
        }).build();
        Trigger trigger = simpleTrigger(jobDetail , exitGraceTimeMillis,0 ,1);
        //记录内容key
        CLEANER_TRIGGER_KEY.add(trigger.getKey());
        QuartzSchedulerManager.getOrCreate().scheduleJob(jobDetail, trigger);
    }
    /**
     * 调度器管理
     */
    private static class QuartzSchedulerManager{
        private static Scheduler instance;
        private static AtomicInteger jobCount;

        private static synchronized Scheduler getOrCreate() throws SchedulerException {
            if(instance == null){
                instance = StdSchedulerFactory.getDefaultScheduler();
                init();
            }
            return instance;
        }

        private static synchronized int incrementAndGetJobCount(){
            return jobCount.incrementAndGet();
        }

        private static synchronized int decrementAndGetJobCount(){
            return jobCount.decrementAndGet();
        }

        private static int getJobCount(){
            return jobCount.get();
        }

        private static synchronized void reset() throws SchedulerException {
            instance.shutdown();
            instance = null;
            jobCount = null;
        }

        private static void init() throws SchedulerException {
            instance.getListenerManager().addSchedulerListener(new SchedulerDelayShutdownListener());
            instance.start();
            jobCount = new AtomicInteger(0);
        }
    }

    /**
     * 调度器延时关闭监听器，该监听器将监听所有任务完成情况，并删除已完成1小时以上到任务，或关闭调度器
     */
    private static class SchedulerDelayShutdownListener implements  SchedulerListener{
        @Override
        public void jobScheduled(Trigger trigger) {
        }
        @Override
        public void jobUnscheduled(TriggerKey triggerKey) {
        }
        @Override
        public void triggerFinalized(Trigger trigger) {
            if(CLEANER_TRIGGER_KEY.contains(trigger.getKey())){
                //清理任务key，删除
                CLEANER_TRIGGER_KEY.remove(trigger.getKey());
            }else{
                //添加job清理任务
                try {
                    addJobCleaner(QuartzSchedulerManager.getOrCreate().getJobDetail(trigger.getJobKey()).getKey() ,SCHEDULER_FREE_TIME);
                } catch (SchedulerException e) {
                }
            }
        }

        @Override
        public void triggerPaused(TriggerKey triggerKey) {
        }
        @Override
        public void triggersPaused(String s) {
        }
        @Override
        public void triggerResumed(TriggerKey triggerKey) {
        }
        @Override
        public void triggersResumed(String s) {
        }
        @Override
        public void jobAdded(JobDetail jobDetail) {
        }
        @Override
        public void jobDeleted(JobKey jobKey) {
        }
        @Override
        public void jobPaused(JobKey jobKey) {
        }
        @Override
        public void jobsPaused(String s) {
        }
        @Override
        public void jobResumed(JobKey jobKey) {
        }
        @Override
        public void jobsResumed(String s) {
        }
        @Override
        public void schedulerError(String s, SchedulerException e) {
        }
        @Override
        public void schedulerInStandbyMode() {
        }
        @Override
        public void schedulerStarted() {
        }
        @Override
        public void schedulerStarting() {
        }
        @Override
        public void schedulerShutdown() {
        }
        @Override
        public void schedulerShuttingdown() {
        }
        @Override
        public void schedulingDataCleared() {
        }
    }
}
